// 例题14  找边界（Find the Border, NEERC 2004, Codeforces Gym100536F）
// 刘汝佳
// 注意：本题可以直接使用“卷包裹”法求出外轮廓。本程序只是为了演示PSLG的实现
#include <vector>
#include <cassert>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <algorithm>

using namespace std;

const double eps = 1e-8;
double dcmp(double x) {
  if (fabs(x) < eps) return 0;
  return x < 0 ? -1 : 1;
}

struct Point {
  double x, y;
  Point(double x = 0, double y = 0): x(x), y(y) { }
};

typedef Point Vector;
Vector operator + (const Vector& A, const Vector& B) 
{ return Vector(A.x + B.x, A.y + B.y); }
Vector operator - (const Point& A, const Point& B) 
{ return Vector(A.x - B.x, A.y - B.y); }
Vector operator * (const Vector& A, double p) 
{ return Vector(A.x * p, A.y * p); }

// 理论上这个“小于”运算符是错的，因为可能有三个点a, b, c, a和b很接近（即a<b好b<a都不成立），
// b和c很接近，但a和c不接近
// 所以使用这种“小于”运算符的前提是能排除上述情况
bool operator < (const Point& a, const Point& b)
{ return dcmp(a.x - b.x) < 0 || (dcmp(a.x - b.x) == 0 && dcmp(a.y - b.y) < 0); }
bool operator == (const Point& a, const Point &b)
{ return dcmp(a.x - b.x) == 0 && dcmp(a.y - b.y) == 0;}

double Dot(const Vector& A, const Vector& B) { return A.x * B.x + A.y * B.y; }
double Cross(const Vector& A, const Vector& B) { return A.x * B.y - A.y * B.x; }
double Length(const Vector& A) { return sqrt(Dot(A, A)); }
typedef vector<Point> Polygon;

Point GetLineIntersection(const Point& P, const Vector& v, const Point& Q, const Vector& w) {
  Vector u = P - Q;
  double t = Cross(w, u) / Cross(v, w);
  return P + v * t;
}

bool SegmentProperIntersection(const Point& a1, const Point& a2, 
  const Point& b1, const Point& b2) {
  double c1 = Cross(a2 - a1, b1 - a1), c2 = Cross(a2 - a1, b2 - a1),
         c3 = Cross(b2 - b1, a1 - b1), c4 = Cross(b2 - b1, a2 - b1);
  return dcmp(c1) * dcmp(c2) < 0 && dcmp(c3) * dcmp(c4) < 0;
}

bool OnSegment(const Point& p, const Point& a1, const Point& a2) {
  return dcmp(Cross(a1 - p, a2 - p)) == 0 && dcmp(Dot(a1 - p, a2 - p)) < 0;
}

// 多边形的有向面积
double PolygonArea(const Polygon& poly) {
  double area = 0;
  int n = poly.size();
  for (int i = 1; i < n - 1; i++)
    area += Cross(poly[i] - poly[0], poly[(i + 1) % n] - poly[0]);
  return area / 2;
}

struct Edge {
  int from, to; // 起点，终点，左边的面编号
  double ang;
};

const int maxn = 10000 + 10; // 最大边数

// 平面直线图（PSLG）实现
struct PSLG {
  int n, m, face_cnt;
  double x[maxn], y[maxn];
  vector<Edge> edges;
  vector<int> G[maxn];
  int vis[maxn * 2]; // 每条边是否已经访问过
  int left[maxn * 2]; // 左面的编号
  int prev[maxn * 2]; // 相同起点的上一条边（即顺时针旋转碰到的下一条边）的编号

  vector<Polygon> faces;
  double area[maxn]; // 每个polygon的面积

  void init(int n) {
    this->n = n;
    for (int i = 0; i < n; i++) G[i].clear();
    edges.clear();
    faces.clear();
  }

  // 有向线段from->to的极角
  double getAngle(int from, int to) {
    return atan2(y[to] - y[from], x[to] - x[from]);
  }

  void AddEdge(int from, int to) {
    edges.push_back((Edge) {from, to, getAngle(from, to)});
    edges.push_back((Edge) {to, from, getAngle(to, from)});
    m = edges.size();
    G[from].push_back(m - 2);
    G[to].push_back(m - 1);
  }

  // 找出faces并计算面积
  void Build() {
    for (int u = 0; u < n; u++) {
      // 给从u出发的各条边按极角排序
      int d = G[u].size();
      for (int i = 0; i < d; i++)
        for (int j = i + 1; j < d; j++) // 这里偷个懒，假设从每个点出发的线段不会太多
          if (edges[G[u][i]].ang > edges[G[u][j]].ang) swap(G[u][i], G[u][j]);
      for (int i = 0; i < d; i++)
        prev[G[u][(i + 1) % d]] = G[u][i];
    }

    memset(vis, 0, sizeof(vis));
    face_cnt = 0;
    for (int u = 0; u < n; u++)
      for (int i = 0; i < G[u].size(); i++) {
        int e = G[u][i];
        if (!vis[e]) { // 逆时针找圈
          face_cnt++;
          Polygon poly;
          for (;;) {
            vis[e] = 1; left[e] = face_cnt;
            int from = edges[e].from;
            poly.push_back(Point(x[from], y[from]));
            e = prev[e ^ 1];
            if (e == G[u][i]) break;
            assert(vis[e] == 0);
          }
          faces.push_back(poly);
        }
      }

    for (int i = 0; i < faces.size(); i++) {
      area[i] = PolygonArea(faces[i]);
    }
  }
};

PSLG g;
const int maxp = 100 + 5;
int n, c;
Point P[maxp], V[maxp * (maxp - 1) / 2 + maxp];
// 在V数组里找到点p
int ID(const Point& p) { return lower_bound(V, V + c, p) - V; }

// 假定poly没有相邻点重合的情况，只需要删除三点共线的情况
Polygon simplify(const Polygon& poly) {
  Polygon ans;
  int n = poly.size();
  for (int i = 0; i < n; i++) {
    Point a = poly[i];
    Point b = poly[(i + 1) % n];
    Point c = poly[(i + 2) % n];
    if (dcmp(Cross(a - b, c - b)) != 0) ans.push_back(b);
  }
  return ans;
}

void build_graph() {
  c = n;
  for (int i = 0; i < n; i++)
    V[i] = P[i];

  vector<double> dist[maxp]; // dist[i][j]是第i条线段上的第j个点离起点（P[i]）的距离
  for (int i = 0; i < n; i++)
    for (int j = i + 1; j < n; j++)
      if (SegmentProperIntersection(P[i], P[(i + 1) % n], P[j], P[(j + 1) % n])) {
        Point p = GetLineIntersection(P[i], P[(i + 1) % n] - P[i], P[j], P[(j + 1) % n] - P[j]);
        V[c++] = p;
        dist[i].push_back(Length(p - P[i]));
        dist[j].push_back(Length(p - P[j]));
      }

  // 为了保证“很接近的点”被看作同一个，这里使用了sort+unique的方法
  // 必须使用前面提到的“理论上是错误”的小于运算符，否则不能保证“很接近的点”在排序后连续排列
  // 另一个常见的处理方式是把坐标扩大很多倍（比如100000倍），然后四舍五入变成整点（计算完毕后再还原），用少许的精度损失换来鲁棒性和速度。
  sort(V, V + c);
  c = unique(V, V + c) - V;

  g.init(c); // c是平面图的点数
  for (int i = 0; i < c; i++) {
    g.x[i] = V[i].x;
    g.y[i] = V[i].y;
  }
  for (int i = 0; i < n; i++) {
    Vector v = P[(i + 1) % n] - P[i];
    double len = Length(v);
    dist[i].push_back(0);
    dist[i].push_back(len);
    sort(dist[i].begin(), dist[i].end());
    int sz = dist[i].size();
    for (int j = 1; j < sz; j++) {
      Point a = P[i] + v * (dist[i][j - 1] / len);
      Point b = P[i] + v * (dist[i][j] / len);
      if (a == b) continue;
      g.AddEdge(ID(a), ID(b));
    }
  }

  g.Build();

  Polygon poly;
  for (int i = 0; i < g.faces.size(); i++)
    if (g.area[i] < 0) { // 对于连通图，惟一一个面积小于零的面是无限面
      poly = g.faces[i];
      reverse(poly.begin(), poly.end()); // 对于内部区域来说，无限面多边形的各个顶点是顺时针的
      poly = simplify(poly); // 无限面多边形上可能会有相邻共线点
      break;
    }

  int m = poly.size();
  printf("%d\n", m);

  // 挑选坐标最小的点作为输出的起点
  int start = 0;
  for (int i = 0; i < m; i++)
    if (poly[i] < poly[start]) start = i;
  for (int i = start; i < m; i++)
    printf("%.4lf %.4lf\n", poly[i].x, poly[i].y);
  for (int i = 0; i < start; i++)
    printf("%.4lf %.4lf\n", poly[i].x, poly[i].y);
}

int main() {
  freopen("find.in", "r", stdin);
  freopen("find.out", "w", stdout);

  while (scanf("%d", &n) == 1 && n) {
    for (int i = 0, x, y; i < n; i++)
      scanf("%d%d", &x, &y), P[i] = Point(x, y);
    build_graph();
  }
  return 0;
}
// 102094879	Dec/23/2020 14:54UTC+8	chenwz	F - Find the Border	GNU C++11	Accepted	31 ms	2100 KB